package com.chengcang.admin.modules.log.binlog.model;

import com.github.shyiko.mysql.binlog.event.EventType;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import lombok.Data;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author fankunfeng
 * @date 2023-01-31 10:45:26
 **/
@Data
public class BinLogItem implements Serializable {
    private static final long serialVersionUID = 5503152746318421290L;

    private Long serverId = null;
    private String dbTable;
    private EventType eventType;
    private Map<String, Column> colums = null;
    private Map<String, Serializable> before = new HashMap<>();
    private Map<String, Serializable> after = new HashMap<>();
    private Long timestamp = null;

    /**
     * 新增或者删除操作数据格式化
     */
    public static BinLogItem itemFromInsertOrDeleted(Serializable[] row, Map<String, Column> columMap, EventType eventType) {
        if (null == row || null == columMap) {
            return null;
        }
        if (row.length != columMap.size()) {
            return null;
        }
        // 初始化Item
        BinLogItem item = new BinLogItem();
        item.eventType = eventType;
        item.colums = columMap;
        item.before = Maps.newHashMap();
        item.after = Maps.newHashMap();

        Map<String, Serializable> beOrAf = Maps.newHashMap();

        columMap.entrySet().forEach(entry -> {
            String key = entry.getKey();
            Column colum = entry.getValue();
            beOrAf.put(key, row[colum.inx]);
        });

        // 写操作放after，删操作放before
        if (EventType.isWrite(eventType)) {
            item.after = beOrAf;
        }
        if (EventType.isDelete(eventType)) {
            item.before = beOrAf;
        }

        return item;
    }

    /**
     * 更新操作数据格式化
     */
    public static BinLogItem itemFromUpdate(Map.Entry<Serializable[], Serializable[]> mapEntry, Map<String, Column> columMap, EventType eventType) {
        if (null == mapEntry || null == columMap) {
            return null;
        }
        // 初始化Item
        BinLogItem item = new BinLogItem();
        item.eventType = eventType;
        item.colums = columMap;
        item.before = Maps.newHashMap();
        item.after = Maps.newHashMap();

        Map<String, Serializable> be = Maps.newHashMap();
        Map<String, Serializable> af = Maps.newHashMap();

        columMap.entrySet().forEach(entry -> {
            String key = entry.getKey();
            Column column = entry.getValue();

            be.put(key, mapEntry.getKey()[column.inx]);

            af.put(key, mapEntry.getValue()[column.inx]);
        });

        MapDifference<String, Serializable> difference = Maps.difference(be, af);
        Map<String, MapDifference.ValueDifference<Serializable>> diffMap = difference.entriesDiffering();

        Map<String, Serializable> left = new HashMap<>();
        Map<String, Serializable> right = new HashMap<>();
        Optional.ofNullable(diffMap.keySet()).ifPresent(key -> {
            left.putAll(diffMap.keySet().stream().collect(Collectors.toMap(Function.identity(), o -> diffMap.get(o).leftValue())));
            left.putAll(diffMap.keySet().stream().collect(Collectors.toMap(Function.identity(), o -> diffMap.get(o).rightValue())));
        });

        item.before.putAll(difference.entriesOnlyOnLeft());
        item.before.putAll(left);
        item.after.putAll(difference.entriesOnlyOnRight());
        item.after.putAll(right);
        return item;
    }

}
